<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 6.3.0">
  <link rel="apple-touch-icon" sizes="180x180" href="/xlrblog/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/xlrblog/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/xlrblog/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/xlrblog/images/logo.svg" color="#222">

<link rel="stylesheet" href="/xlrblog/css/main.css">


<link rel="stylesheet" href="/xlrblog/lib/font-awesome/css/all.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"gitee.com","root":"/xlrblog/","scheme":"Pisces","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":true,"show_result":true,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},"path":"search.json"};
  </script>

  <meta name="description" content="分布式事务 1.分布式事务问题1.1.本地事务本地事务，也就是传统的单机事务。在传统数据库事务中，必须要满足四个原则：  1.2.分布式事务分布式事务，就是指不是在单个服务或单个数据库架构下，产生的事务，例如：  跨数据源的分布式事务 跨服务的分布式事务 综合情况  在数据库水平拆分、服务垂直拆分之后，一个业务操作通常要跨多个数据库、服务才能完成。例如电商行业中比较常见的下单付款案例，包括下面几个">
<meta property="og:type" content="article">
<meta property="og:title" content="分布式事务">
<meta property="og:url" content="https://gitee.com/xlr0306/2023/10/29/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1/index.html">
<meta property="og:site_name" content="君不见的博客">
<meta property="og:description" content="分布式事务 1.分布式事务问题1.1.本地事务本地事务，也就是传统的单机事务。在传统数据库事务中，必须要满足四个原则：  1.2.分布式事务分布式事务，就是指不是在单个服务或单个数据库架构下，产生的事务，例如：  跨数据源的分布式事务 跨服务的分布式事务 综合情况  在数据库水平拆分、服务垂直拆分之后，一个业务操作通常要跨多个数据库、服务才能完成。例如电商行业中比较常见的下单付款案例，包括下面几个">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724165045186.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724165338958.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724165634571.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724165709994.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724165729273.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724170113404.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724170517944.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724170704694.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724170735847.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724170834855.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724170932072.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724171007516.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724171041210.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724171546472.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724172123567.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724172225817.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724172326452.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724172549013.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724173654258.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724174102768.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724174234987.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724174424070.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724174859556.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724175327511.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724180722921.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724181541234.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724181843029.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724182217272.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724182424907.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724182457951.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724182706011.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724182810734.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724182937713.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724183426891.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724184846396.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724185021819.png">
<meta property="og:image" content="https://gitee.com/xlrblog/assets/image-20210724185240957.png">
<meta property="article:published_time" content="2023-10-29T08:42:56.000Z">
<meta property="article:modified_time" content="2023-10-29T08:44:09.967Z">
<meta property="article:author" content="君不见">
<meta property="article:tag" content="SpringCloud">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://gitee.com/xlrblog/assets/image-20210724165045186.png">

<link rel="canonical" href="https://gitee.com/xlr0306/2023/10/29/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh-CN'
  };
</script>

  <title>分布式事务 | 君不见的博客</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/xlrblog/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">君不见的博客</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
      <p class="site-subtitle" itemprop="description">记录生活中的点点滴滴</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/xlrblog/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/xlrblog/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/xlrblog/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/xlrblog/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a>

  </li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup">
        <div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div id="search-result">
  <div id="no-result">
    <i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>
  </div>
</div>

    </div>
  </div>

</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content post posts-expand">
            

    
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://gitee.com/xlr0306/2023/10/29/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/xlrblog/images/avatar.gif">
      <meta itemprop="name" content="君不见">
      <meta itemprop="description" content="君不见，黄河之水天上来，奔流到海不复回。">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="君不见的博客">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          分布式事务
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
              

              <time title="创建时间：2023-10-29 16:42:56 / 修改时间：16:44:09" itemprop="dateCreated datePublished" datetime="2023-10-29T16:42:56+08:00">2023-10-29</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/xlrblog/categories/Java/" itemprop="url" rel="index"><span itemprop="name">Java</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/xlrblog/categories/Java/SpringCloud/" itemprop="url" rel="index"><span itemprop="name">SpringCloud</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
        <p>分布式事务</p>
<h1 id="1-分布式事务问题"><a href="#1-分布式事务问题" class="headerlink" title="1.分布式事务问题"></a>1.分布式事务问题</h1><h2 id="1-1-本地事务"><a href="#1-1-本地事务" class="headerlink" title="1.1.本地事务"></a>1.1.本地事务</h2><p>本地事务，也就是传统的<strong>单机事务</strong>。在传统数据库事务中，必须要满足四个原则：</p>
<p><img src="/xlrblog/assets/image-20210724165045186.png" alt="image-20210724165045186"></p>
<h2 id="1-2-分布式事务"><a href="#1-2-分布式事务" class="headerlink" title="1.2.分布式事务"></a>1.2.分布式事务</h2><p><strong>分布式事务</strong>，就是指不是在单个服务或单个数据库架构下，产生的事务，例如：</p>
<ul>
<li>跨数据源的分布式事务</li>
<li>跨服务的分布式事务</li>
<li>综合情况</li>
</ul>
<p>在数据库水平拆分、服务垂直拆分之后，一个业务操作通常要跨多个数据库、服务才能完成。例如电商行业中比较常见的下单付款案例，包括下面几个行为：</p>
<ul>
<li>创建新订单</li>
<li>扣减商品库存</li>
<li>从用户账户余额扣除金额</li>
</ul>
<p>完成上面的操作需要访问三个不同的微服务和三个不同的数据库。</p>
<p><img src="/xlrblog/assets/image-20210724165338958.png" alt="image-20210724165338958"></p>
<p>订单的创建、库存的扣减、账户扣款在每一个服务和数据库内是一个本地事务，可以保证ACID原则。</p>
<p>但是当我们把三件事情看做一个”业务”，要满足保证“业务”的原子性，要么所有操作全部成功，要么全部失败，不允许出现部分成功部分失败的现象，这就是<strong>分布式系统下的事务</strong>了。</p>
<p>此时ACID难以满足，这是分布式事务要解决的问题</p>
<h2 id="1-3-演示分布式事务问题"><a href="#1-3-演示分布式事务问题" class="headerlink" title="1.3.演示分布式事务问题"></a>1.3.演示分布式事务问题</h2><p>我们通过一个案例来演示分布式事务的问题：</p>
<p>1）<strong>创建数据库，名为seata_demo，然后导入课前资料提供的SQL文件：</strong></p>
<p><img src="/xlrblog/assets/image-20210724165634571.png" alt="image-20210724165634571"> </p>
<p>2）<strong>导入课前资料提供的微服务：</strong></p>
<p><img src="/xlrblog/assets/image-20210724165709994.png" alt="image-20210724165709994"> </p>
<p>微服务结构如下：</p>
<p><img src="/xlrblog/assets/image-20210724165729273.png" alt="image-20210724165729273"> </p>
<p>其中：</p>
<p>seata-demo：父工程，负责管理项目依赖</p>
<ul>
<li>account-service：账户服务，负责管理用户的资金账户。提供扣减余额的接口</li>
<li>storage-service：库存服务，负责管理商品库存。提供扣减库存的接口</li>
<li>order-service：订单服务，负责管理订单。创建订单时，需要调用account-service和storage-service</li>
</ul>
<p><strong>3）启动nacos、所有微服务</strong></p>
<p><strong>4）测试下单功能，发出Post请求：</strong></p>
<p>请求如下：</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl --location --request POST <span class="string">&#x27;http://localhost:8082/order?userId=user202103032042012&amp;commodityCode=100202003032041&amp;count=20&amp;money=200&#x27;</span></span><br></pre></td></tr></table></figure>

<p>如图：</p>
<p><img src="/xlrblog/assets/image-20210724170113404.png" alt="image-20210724170113404"></p>
<p>测试发现，当库存不足时，如果余额已经扣减，并不会回滚，出现了分布式事务问题。</p>
<h1 id="2-理论基础"><a href="#2-理论基础" class="headerlink" title="2.理论基础"></a>2.理论基础</h1><p>解决分布式事务问题，需要一些分布式系统的基础知识作为理论指导。</p>
<h2 id="2-1-CAP定理"><a href="#2-1-CAP定理" class="headerlink" title="2.1.CAP定理"></a>2.1.CAP定理</h2><p>1998年，加州大学的计算机科学家 Eric Brewer 提出，分布式系统有三个指标。</p>
<blockquote>
<ul>
<li>Consistency（一致性）</li>
<li>Availability（可用性）</li>
<li>Partition tolerance （分区容错性）</li>
</ul>
</blockquote>
<p><img src="/xlrblog/assets/image-20210724170517944.png" alt="image-20210724170517944"></p>
<p>它们的第一个字母分别是 C、A、P。</p>
<p>Eric Brewer 说，这三个指标不可能同时做到。这个结论就叫做 CAP 定理。</p>
<h3 id="2-1-1-一致性"><a href="#2-1-1-一致性" class="headerlink" title="2.1.1.一致性"></a>2.1.1.一致性</h3><p>Consistency（一致性）：用户访问分布式系统中的任意节点，得到的数据必须一致。</p>
<p>比如现在包含两个节点，其中的初始数据是一致的：</p>
<p><img src="/xlrblog/assets/image-20210724170704694.png" alt="image-20210724170704694"></p>
<p>当我们修改其中一个节点的数据时，两者的数据产生了差异：</p>
<p><img src="/xlrblog/assets/image-20210724170735847.png" alt="image-20210724170735847"></p>
<p>要想保住一致性，就必须实现node01 到 node02的数据 同步：</p>
<p><img src="/xlrblog/assets/image-20210724170834855.png" alt="image-20210724170834855"></p>
<h3 id="2-1-2-可用性"><a href="#2-1-2-可用性" class="headerlink" title="2.1.2.可用性"></a>2.1.2.可用性</h3><p>Availability （可用性）：用户访问集群中的任意健康节点，必须能得到响应，而不是超时或拒绝。</p>
<p>如图，有三个节点的集群，访问任何一个都可以及时得到响应：</p>
<p><img src="/xlrblog/assets/image-20210724170932072.png" alt="image-20210724170932072"></p>
<p>当有部分节点因为网络故障或其它原因无法访问时，代表节点不可用：</p>
<p><img src="/xlrblog/assets/image-20210724171007516.png" alt="image-20210724171007516"></p>
<h3 id="2-1-3-分区容错"><a href="#2-1-3-分区容错" class="headerlink" title="2.1.3.分区容错"></a>2.1.3.分区容错</h3><p><strong>Partition（分区）</strong>：因为网络故障或其它原因导致分布式系统中的部分节点与其它节点失去连接，形成独立分区。</p>
<p><img src="/xlrblog/assets/image-20210724171041210.png" alt="image-20210724171041210"></p>
<p><strong>Tolerance（容错）</strong>：在集群出现分区时，整个系统也要持续对外提供服务</p>
<h3 id="2-1-4-矛盾"><a href="#2-1-4-矛盾" class="headerlink" title="2.1.4.矛盾"></a>2.1.4.矛盾</h3><p>在分布式系统中，系统间的网络不能100%保证健康，一定会有故障的时候，而服务有必须对外保证服务。因此Partition Tolerance不可避免。</p>
<p>当节点接收到新的数据变更时，就会出现问题了：</p>
<p><img src="/xlrblog/assets/image-20210724171546472.png" alt="image-20210724171546472"></p>
<p>如果此时要保证<strong>一致性</strong>，就必须等待网络恢复，完成数据同步后，整个集群才对外提供服务，服务处于阻塞状态，不可用。</p>
<p>如果此时要保证<strong>可用性</strong>，就不能等待网络恢复，那node01、node02与node03之间就会出现数据不一致。</p>
<p>也就是说，在P一定会出现的情况下，A和C之间只能实现一个。</p>
<h2 id="2-2-BASE理论"><a href="#2-2-BASE理论" class="headerlink" title="2.2.BASE理论"></a>2.2.BASE理论</h2><p>BASE理论是对CAP的一种解决思路，包含三个思想：</p>
<ul>
<li><strong>Basically Available</strong> <strong>（基本可用）</strong>：分布式系统在出现故障时，允许损失部分可用性，即保证核心可用。</li>
<li><strong>Soft State（软状态）：</strong>在一定时间内，允许出现中间状态，比如临时的不一致状态。</li>
<li><strong>Eventually Consistent（最终一致性）</strong>：虽然无法保证强一致性，但是在软状态结束后，最终达到数据一致。</li>
</ul>
<h2 id="2-3-解决分布式事务的思路"><a href="#2-3-解决分布式事务的思路" class="headerlink" title="2.3.解决分布式事务的思路"></a>2.3.解决分布式事务的思路</h2><p>分布式事务最大的问题是各个子事务的一致性问题，因此可以借鉴CAP定理和BASE理论，有两种解决思路：</p>
<ul>
<li><p>AP模式：各子事务分别执行和提交，允许出现结果不一致，然后采用弥补措施恢复数据即可，实现最终一致。</p>
</li>
<li><p>CP模式：各个子事务执行后互相等待，同时提交，同时回滚，达成强一致。但事务等待过程中，处于弱可用状态。</p>
</li>
</ul>
<p>但不管是哪一种模式，都需要在子系统事务之间互相通讯，协调事务状态，也就是需要一个**事务协调者(TC)**：</p>
<p><img src="/xlrblog/assets/image-20210724172123567.png" alt="image-20210724172123567"></p>
<p>这里的子系统事务，称为<strong>分支事务</strong>；有关联的各个分支事务在一起称为<strong>全局事务</strong>。</p>
<h1 id="3-初识Seata"><a href="#3-初识Seata" class="headerlink" title="3.初识Seata"></a>3.初识Seata</h1><p>Seata是 2019 年 1 月份蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案。致力于提供高性能和简单易用的分布式事务服务，为用户打造一站式的分布式解决方案。</p>
<p>官网地址：<a target="_blank" rel="noopener" href="http://seata.io/%EF%BC%8C%E5%85%B6%E4%B8%AD%E7%9A%84%E6%96%87%E6%A1%A3%E3%80%81%E6%92%AD%E5%AE%A2%E4%B8%AD%E6%8F%90%E4%BE%9B%E4%BA%86%E5%A4%A7%E9%87%8F%E7%9A%84%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E%E3%80%81%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E3%80%82">http://seata.io/，其中的文档、播客中提供了大量的使用说明、源码分析。</a></p>
<p><img src="/xlrblog/assets/image-20210724172225817.png" alt="image-20210724172225817"></p>
<h2 id="3-1-Seata的架构"><a href="#3-1-Seata的架构" class="headerlink" title="3.1.Seata的架构"></a>3.1.Seata的架构</h2><p>Seata事务管理中有三个重要的角色：</p>
<ul>
<li><p><strong>TC (Transaction Coordinator) -</strong> <strong>事务协调者：</strong>维护全局和分支事务的状态，协调全局事务提交或回滚。</p>
</li>
<li><p><strong>TM (Transaction Manager) -</strong> <strong>事务管理器：</strong>定义全局事务的范围、开始全局事务、提交或回滚全局事务。</p>
</li>
<li><p><strong>RM (Resource Manager) -</strong> <strong>资源管理器：</strong>管理分支事务处理的资源，与TC交谈以注册分支事务和报告分支事务的状态，并驱动分支事务提交或回滚。</p>
</li>
</ul>
<p>整体的架构如图：</p>
<p><img src="/xlrblog/assets/image-20210724172326452.png" alt="image-20210724172326452"></p>
<p>Seata基于上述架构提供了四种不同的分布式事务解决方案：</p>
<ul>
<li>XA模式：强一致性分阶段事务模式，牺牲了一定的可用性，无业务侵入</li>
<li>TCC模式：最终一致的分阶段事务模式，有业务侵入</li>
<li>AT模式：最终一致的分阶段事务模式，无业务侵入，也是Seata的默认模式</li>
<li>SAGA模式：长事务模式，有业务侵入</li>
</ul>
<p>无论哪种方案，都离不开TC，也就是事务的协调者。</p>
<h2 id="3-2-部署TC服务"><a href="#3-2-部署TC服务" class="headerlink" title="3.2.部署TC服务"></a>3.2.部署TC服务</h2><p>参考课前资料提供的文档《 seata的部署和集成.md 》：</p>
<p><img src="/xlrblog/assets/image-20210724172549013.png" alt="image-20210724172549013"></p>
<h2 id="3-3-微服务集成Seata"><a href="#3-3-微服务集成Seata" class="headerlink" title="3.3.微服务集成Seata"></a>3.3.微服务集成Seata</h2><p>我们以order-service为例来演示。</p>
<h3 id="3-3-1-引入依赖"><a href="#3-3-1-引入依赖" class="headerlink" title="3.3.1.引入依赖"></a>3.3.1.引入依赖</h3><p>首先，在order-service中引入依赖：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--seata--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.alibaba.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-alibaba-seata<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">exclusions</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!--版本较低，1.3.0，因此排除--&gt;</span> </span><br><span class="line">        <span class="tag">&lt;<span class="name">exclusion</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>seata-spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>io.seata<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">exclusion</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">exclusions</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>io.seata<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>seata-spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--seata starter 采用1.4.2版本--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>$&#123;seata.version&#125;<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>



<h3 id="3-3-2-配置TC地址"><a href="#3-3-2-配置TC地址" class="headerlink" title="3.3.2.配置TC地址"></a>3.3.2.配置TC地址</h3><p>在order-service中的application.yml中，配置TC服务信息，通过注册中心nacos，结合服务名称获取TC地址：</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">seata:</span></span><br><span class="line">  <span class="attr">registry:</span> <span class="comment"># TC服务注册中心的配置，微服务根据这些信息去注册中心获取tc服务地址</span></span><br><span class="line">    <span class="attr">type:</span> <span class="string">nacos</span> <span class="comment"># 注册中心类型 nacos</span></span><br><span class="line">    <span class="attr">nacos:</span></span><br><span class="line">      <span class="attr">server-addr:</span> <span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span><span class="string">:8848</span> <span class="comment"># nacos地址</span></span><br><span class="line">      <span class="attr">namespace:</span> <span class="string">&quot;&quot;</span> <span class="comment"># namespace，默认为空</span></span><br><span class="line">      <span class="attr">group:</span> <span class="string">DEFAULT_GROUP</span> <span class="comment"># 分组，默认是DEFAULT_GROUP</span></span><br><span class="line">      <span class="attr">application:</span> <span class="string">seata-tc-server</span> <span class="comment"># seata服务名称</span></span><br><span class="line">      <span class="attr">username:</span> <span class="string">nacos</span></span><br><span class="line">      <span class="attr">password:</span> <span class="string">nacos</span></span><br><span class="line">  <span class="attr">tx-service-group:</span> <span class="string">seata-demo</span> <span class="comment"># 事务组名称</span></span><br><span class="line">  <span class="attr">service:</span></span><br><span class="line">    <span class="attr">vgroup-mapping:</span> <span class="comment"># 事务组与cluster的映射关系</span></span><br><span class="line">      <span class="attr">seata-demo:</span> <span class="string">SH</span></span><br></pre></td></tr></table></figure>



<p>微服务如何根据这些配置寻找TC的地址呢？</p>
<p>我们知道注册到Nacos中的微服务，确定一个具体实例需要四个信息：</p>
<ul>
<li>namespace：命名空间</li>
<li>group：分组</li>
<li>application：服务名</li>
<li>cluster：集群名</li>
</ul>
<p>以上四个信息，在刚才的yaml文件中都能找到：</p>
<p><img src="/xlrblog/assets/image-20210724173654258.png" alt="image-20210724173654258"></p>
<p>namespace为空，就是默认的public</p>
<p>结合起来，TC服务的信息就是：public@DEFAULT_GROUP@seata-tc-server@SH，这样就能确定TC服务集群了。然后就可以去Nacos拉取对应的实例信息了。</p>
<h3 id="3-3-3-其它服务"><a href="#3-3-3-其它服务" class="headerlink" title="3.3.3.其它服务"></a>3.3.3.其它服务</h3><p>其它两个微服务也都参考order-service的步骤来做，完全一样。</p>
<h1 id="4-动手实践"><a href="#4-动手实践" class="headerlink" title="4.动手实践"></a>4.动手实践</h1><p>下面我们就一起学习下Seata中的四种不同的事务模式。</p>
<h2 id="4-1-XA模式"><a href="#4-1-XA模式" class="headerlink" title="4.1.XA模式"></a>4.1.XA模式</h2><p>XA 规范 是 X&#x2F;Open 组织定义的分布式事务处理（DTP，Distributed Transaction Processing）标准，XA 规范 描述了全局的TM与局部的RM之间的接口，几乎所有主流的数据库都对 XA 规范 提供了支持。</p>
<h3 id="4-1-1-两阶段提交"><a href="#4-1-1-两阶段提交" class="headerlink" title="4.1.1.两阶段提交"></a>4.1.1.两阶段提交</h3><p>XA是规范，目前主流数据库都实现了这种规范，实现的原理都是基于两阶段提交。</p>
<p>正常情况：</p>
<p><img src="/xlrblog/assets/image-20210724174102768.png" alt="image-20210724174102768"></p>
<p>异常情况：</p>
<p><img src="/xlrblog/assets/image-20210724174234987.png" alt="image-20210724174234987"></p>
<p>一阶段：</p>
<ul>
<li>事务协调者通知每个事物参与者执行本地事务</li>
<li>本地事务执行完成后报告事务执行状态给事务协调者，此时事务不提交，继续持有数据库锁</li>
</ul>
<p>二阶段：</p>
<ul>
<li>事务协调者基于一阶段的报告来判断下一步操作<ul>
<li>如果一阶段都成功，则通知所有事务参与者，提交事务</li>
<li>如果一阶段任意一个参与者失败，则通知所有事务参与者回滚事务</li>
</ul>
</li>
</ul>
<h3 id="4-1-2-Seata的XA模型"><a href="#4-1-2-Seata的XA模型" class="headerlink" title="4.1.2.Seata的XA模型"></a>4.1.2.Seata的XA模型</h3><p>Seata对原始的XA模式做了简单的封装和改造，以适应自己的事务模型，基本架构如图：</p>
<p><img src="/xlrblog/assets/image-20210724174424070.png" alt="image-20210724174424070"></p>
<p>RM一阶段的工作：</p>
<p>​	① 注册分支事务到TC</p>
<p>​	② 执行分支业务sql但不提交</p>
<p>​	③ 报告执行状态到TC</p>
<p>TC二阶段的工作：</p>
<ul>
<li><p>TC检测各分支事务执行状态</p>
<p>a.如果都成功，通知所有RM提交事务</p>
<p>b.如果有失败，通知所有RM回滚事务</p>
</li>
</ul>
<p>RM二阶段的工作：</p>
<ul>
<li>接收TC指令，提交或回滚事务</li>
</ul>
<h3 id="4-1-3-优缺点"><a href="#4-1-3-优缺点" class="headerlink" title="4.1.3.优缺点"></a>4.1.3.优缺点</h3><p>XA模式的优点是什么？</p>
<ul>
<li>事务的强一致性，满足ACID原则。</li>
<li>常用数据库都支持，实现简单，并且没有代码侵入</li>
</ul>
<p>XA模式的缺点是什么？</p>
<ul>
<li>因为一阶段需要锁定数据库资源，等待二阶段结束才释放，性能较差</li>
<li>依赖关系型数据库实现事务</li>
</ul>
<h3 id="4-1-4-实现XA模式"><a href="#4-1-4-实现XA模式" class="headerlink" title="4.1.4.实现XA模式"></a>4.1.4.实现XA模式</h3><p>Seata的starter已经完成了XA模式的自动装配，实现非常简单，步骤如下：</p>
<p>1）修改application.yml文件（每个参与事务的微服务），开启XA模式：</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">seata:</span></span><br><span class="line">  <span class="attr">data-source-proxy-mode:</span> <span class="string">XA</span></span><br></pre></td></tr></table></figure>



<p>2）给发起全局事务的入口方法添加@GlobalTransactional注解:</p>
<p>本例中是OrderServiceImpl中的create方法.</p>
<p><img src="/xlrblog/assets/image-20210724174859556.png" alt="image-20210724174859556"></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> cn.itcast.order.service.impl;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> cn.itcast.order.client.AccountClient;</span><br><span class="line"><span class="keyword">import</span> cn.itcast.order.client.StorageClient;</span><br><span class="line"><span class="keyword">import</span> cn.itcast.order.entity.Order;</span><br><span class="line"><span class="keyword">import</span> cn.itcast.order.mapper.OrderMapper;</span><br><span class="line"><span class="keyword">import</span> cn.itcast.order.service.OrderService;</span><br><span class="line"><span class="keyword">import</span> feign.FeignException;</span><br><span class="line"><span class="keyword">import</span> io.seata.spring.annotation.GlobalTransactional;</span><br><span class="line"><span class="keyword">import</span> lombok.extern.slf4j.Slf4j;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Service;</span><br><span class="line"><span class="keyword">import</span> org.springframework.transaction.annotation.Transactional;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> 虎哥</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">OrderService</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> AccountClient accountClient;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> StorageClient storageClient;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> OrderMapper orderMapper;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">OrderServiceImpl</span><span class="params">(AccountClient accountClient, StorageClient storageClient, OrderMapper orderMapper)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.accountClient = accountClient;</span><br><span class="line">        <span class="built_in">this</span>.storageClient = storageClient;</span><br><span class="line">        <span class="built_in">this</span>.orderMapper = orderMapper;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line"><span class="comment">//    @Transactional</span></span><br><span class="line">    <span class="meta">@GlobalTransactional</span></span><br><span class="line">    <span class="keyword">public</span> Long <span class="title function_">create</span><span class="params">(Order order)</span> &#123;</span><br><span class="line">        <span class="comment">// 创建订单</span></span><br><span class="line">        orderMapper.insert(order);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 扣用户余额</span></span><br><span class="line">            accountClient.deduct(order.getUserId(), order.getMoney());</span><br><span class="line">            <span class="comment">// 扣库存</span></span><br><span class="line">            storageClient.deduct(order.getCommodityCode(), order.getCount());</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">catch</span> (FeignException e) &#123;</span><br><span class="line">            log.error(<span class="string">&quot;下单失败，原因:&#123;&#125;&quot;</span>, e.contentUTF8(), e);</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e.contentUTF8(), e);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> order.getId();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>3）重启服务并测试</p>
<p>重启order-service，再次测试，发现无论怎样，三个微服务都能成功回滚。</p>
<h2 id="4-2-AT模式"><a href="#4-2-AT模式" class="headerlink" title="4.2.AT模式"></a>4.2.AT模式</h2><p>AT模式同样是分阶段提交的事务模型，不过缺弥补了XA模型中资源锁定周期过长的缺陷。</p>
<h3 id="4-2-1-Seata的AT模型"><a href="#4-2-1-Seata的AT模型" class="headerlink" title="4.2.1.Seata的AT模型"></a>4.2.1.Seata的AT模型</h3><p>基本流程图：</p>
<p><img src="/xlrblog/assets/image-20210724175327511.png" alt="image-20210724175327511"></p>
<p>阶段一RM的工作：</p>
<ul>
<li>注册分支事务</li>
<li>记录undo-log（数据快照）</li>
<li>执行业务sql并提交</li>
<li>报告事务状态</li>
</ul>
<p>阶段二提交时RM的工作：</p>
<ul>
<li>删除undo-log即可</li>
</ul>
<p>阶段二回滚时RM的工作：</p>
<ul>
<li>根据undo-log恢复数据到更新前</li>
</ul>
<h3 id="4-2-2-流程梳理"><a href="#4-2-2-流程梳理" class="headerlink" title="4.2.2.流程梳理"></a>4.2.2.流程梳理</h3><p>我们用一个真实的业务来梳理下AT模式的原理。</p>
<p>比如，现在又一个数据库表，记录用户余额：</p>
<table>
<thead>
<tr>
<th><strong>id</strong></th>
<th><strong>money</strong></th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>100</td>
</tr>
</tbody></table>
<p>其中一个分支业务要执行的SQL为：</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">update</span> tb_account <span class="keyword">set</span> money <span class="operator">=</span> money <span class="operator">-</span> <span class="number">10</span> <span class="keyword">where</span> id <span class="operator">=</span> <span class="number">1</span></span><br></pre></td></tr></table></figure>



<p>AT模式下，当前分支事务执行流程如下：</p>
<p>一阶段：</p>
<p>1）TM发起并注册全局事务到TC</p>
<p>2）TM调用分支事务</p>
<p>3）分支事务准备执行业务SQL</p>
<p>4）RM拦截业务SQL，根据where条件查询原始数据，形成快照。</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;id&quot;</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span> <span class="attr">&quot;money&quot;</span><span class="punctuation">:</span> <span class="number">100</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure>

<p>5）RM执行业务SQL，提交本地事务，释放数据库锁。此时 <code>money = 90</code></p>
<p>6）RM报告本地事务状态给TC</p>
<p>二阶段：</p>
<p>1）TM通知TC事务结束</p>
<p>2）TC检查分支事务状态</p>
<p>​	 a）如果都成功，则立即删除快照</p>
<p>​	 b）如果有分支事务失败，需要回滚。读取快照数据（<code>&#123;&quot;id&quot;: 1, &quot;money&quot;: 100&#125;</code>），将快照恢复到数据库。此时数据库再次恢复为100</p>
<p>流程图：</p>
<p><img src="/xlrblog/assets/image-20210724180722921.png" alt="image-20210724180722921"></p>
<h3 id="4-2-3-AT与XA的区别"><a href="#4-2-3-AT与XA的区别" class="headerlink" title="4.2.3.AT与XA的区别"></a>4.2.3.AT与XA的区别</h3><p>简述AT模式与XA模式最大的区别是什么？</p>
<ul>
<li>XA模式一阶段不提交事务，锁定资源；AT模式一阶段直接提交，不锁定资源。</li>
<li>XA模式依赖数据库机制实现回滚；AT模式利用数据快照实现数据回滚。</li>
<li>XA模式强一致；AT模式最终一致</li>
</ul>
<h3 id="4-2-4-脏写问题"><a href="#4-2-4-脏写问题" class="headerlink" title="4.2.4.脏写问题"></a>4.2.4.脏写问题</h3><p>在多线程并发访问AT模式的分布式事务时，有可能出现脏写问题，如图：</p>
<p><img src="/xlrblog/assets/image-20210724181541234.png" alt="image-20210724181541234"></p>
<p>解决思路就是引入了全局锁的概念。在释放DB锁之前，先拿到全局锁。避免同一时刻有另外一个事务来操作当前数据。</p>
<p><img src="/xlrblog/assets/image-20210724181843029.png" alt="image-20210724181843029"></p>
<h3 id="4-2-5-优缺点"><a href="#4-2-5-优缺点" class="headerlink" title="4.2.5.优缺点"></a>4.2.5.优缺点</h3><p>AT模式的优点：</p>
<ul>
<li>一阶段完成直接提交事务，释放数据库资源，性能比较好</li>
<li>利用全局锁实现读写隔离</li>
<li>没有代码侵入，框架自动完成回滚和提交</li>
</ul>
<p>AT模式的缺点：</p>
<ul>
<li>两阶段之间属于软状态，属于最终一致</li>
<li>框架的快照功能会影响性能，但比XA模式要好很多</li>
</ul>
<h3 id="4-2-6-实现AT模式"><a href="#4-2-6-实现AT模式" class="headerlink" title="4.2.6.实现AT模式"></a>4.2.6.实现AT模式</h3><p>AT模式中的快照生成、回滚等动作都是由框架自动完成，没有任何代码侵入，因此实现非常简单。</p>
<p>只不过，AT模式需要一个表来记录全局锁、另一张表来记录数据快照undo_log。</p>
<p>1）导入数据库表，记录全局锁</p>
<p>导入课前资料提供的Sql文件：seata-at.sql，其中lock_table导入到TC服务关联的数据库，undo_log表导入到微服务关联的数据库：</p>
<p><img src="/xlrblog/assets/image-20210724182217272.png" alt="image-20210724182217272"></p>
<p>2）修改application.yml文件，将事务模式修改为AT模式即可：</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">seata:</span></span><br><span class="line">  <span class="attr">data-source-proxy-mode:</span> <span class="string">AT</span> <span class="comment"># 默认就是AT</span></span><br></pre></td></tr></table></figure>



<p>3）重启服务并测试</p>
<h2 id="4-3-TCC模式"><a href="#4-3-TCC模式" class="headerlink" title="4.3.TCC模式"></a>4.3.TCC模式</h2><p>TCC模式与AT模式非常相似，每阶段都是独立事务，不同的是TCC通过人工编码来实现数据恢复。需要实现三个方法：</p>
<ul>
<li><p>Try：资源的检测和预留； </p>
</li>
<li><p>Confirm：完成资源操作业务；要求 Try 成功 Confirm 一定要能成功。</p>
</li>
<li><p>Cancel：预留资源释放，可以理解为try的反向操作。</p>
</li>
</ul>
<h3 id="4-3-1-流程分析"><a href="#4-3-1-流程分析" class="headerlink" title="4.3.1.流程分析"></a>4.3.1.流程分析</h3><p>举例，一个扣减用户余额的业务。假设账户A原来余额是100，需要余额扣减30元。</p>
<ul>
<li><strong>阶段一（ Try ）</strong>：检查余额是否充足，如果充足则冻结金额增加30元，可用余额扣除30</li>
</ul>
<p>初始余额：</p>
<p><img src="/xlrblog/assets/image-20210724182424907.png" alt="image-20210724182424907"></p>
<p>余额充足，可以冻结：</p>
<p><img src="/xlrblog/assets/image-20210724182457951.png" alt="image-20210724182457951"></p>
<p>此时，总金额 &#x3D; 冻结金额 + 可用金额，数量依然是100不变。事务直接提交无需等待其它事务。</p>
<ul>
<li>**阶段二（Confirm)**：假如要提交（Confirm），则冻结金额扣减30</li>
</ul>
<p>确认可以提交，不过之前可用金额已经扣减过了，这里只要清除冻结金额就好了：</p>
<p><img src="/xlrblog/assets/image-20210724182706011.png" alt="image-20210724182706011"></p>
<p>此时，总金额 &#x3D; 冻结金额 + 可用金额 &#x3D; 0 + 70  &#x3D; 70元</p>
<ul>
<li>**阶段二(Canncel)**：如果要回滚（Cancel），则冻结金额扣减30，可用余额增加30</li>
</ul>
<p>需要回滚，那么就要释放冻结金额，恢复可用金额：</p>
<p><img src="/xlrblog/assets/image-20210724182810734.png" alt="image-20210724182810734"></p>
<h3 id="4-3-2-Seata的TCC模型"><a href="#4-3-2-Seata的TCC模型" class="headerlink" title="4.3.2.Seata的TCC模型"></a>4.3.2.Seata的TCC模型</h3><p>Seata中的TCC模型依然延续之前的事务架构，如图：</p>
<p><img src="/xlrblog/assets/image-20210724182937713.png" alt="image-20210724182937713"></p>
<h3 id="4-3-3-优缺点"><a href="#4-3-3-优缺点" class="headerlink" title="4.3.3.优缺点"></a>4.3.3.优缺点</h3><p>TCC模式的每个阶段是做什么的？</p>
<ul>
<li>Try：资源检查和预留</li>
<li>Confirm：业务执行和提交</li>
<li>Cancel：预留资源的释放</li>
</ul>
<p>TCC的优点是什么？</p>
<ul>
<li>一阶段完成直接提交事务，释放数据库资源，性能好</li>
<li>相比AT模型，无需生成快照，无需使用全局锁，性能最强</li>
<li>不依赖数据库事务，而是依赖补偿操作，可以用于非事务型数据库</li>
</ul>
<p>TCC的缺点是什么？</p>
<ul>
<li>有代码侵入，需要人为编写try、Confirm和Cancel接口，太麻烦</li>
<li>软状态，事务是最终一致</li>
<li>需要考虑Confirm和Cancel的失败情况，做好幂等处理</li>
</ul>
<h3 id="4-3-4-事务悬挂和空回滚"><a href="#4-3-4-事务悬挂和空回滚" class="headerlink" title="4.3.4.事务悬挂和空回滚"></a>4.3.4.事务悬挂和空回滚</h3><h4 id="1）空回滚"><a href="#1）空回滚" class="headerlink" title="1）空回滚"></a>1）空回滚</h4><p>当某分支事务的try阶段<strong>阻塞</strong>时，可能导致全局事务超时而触发二阶段的cancel操作。在未执行try操作时先执行了cancel操作，这时cancel不能做回滚，就是<strong>空回滚</strong>。</p>
<p>如图：</p>
<p><img src="/xlrblog/assets/image-20210724183426891.png" alt="image-20210724183426891"></p>
<p>执行cancel操作时，应当判断try是否已经执行，如果尚未执行，则应该空回滚。</p>
<h4 id="2）业务悬挂"><a href="#2）业务悬挂" class="headerlink" title="2）业务悬挂"></a>2）业务悬挂</h4><p>对于已经空回滚的业务，之前被阻塞的try操作恢复，继续执行try，就永远不可能confirm或cancel ，事务一直处于中间状态，这就是<strong>业务悬挂</strong>。</p>
<p>执行try操作时，应当判断cancel是否已经执行过了，如果已经执行，应当阻止空回滚后的try操作，避免悬挂</p>
<h3 id="4-3-5-实现TCC模式"><a href="#4-3-5-实现TCC模式" class="headerlink" title="4.3.5.实现TCC模式"></a>4.3.5.实现TCC模式</h3><p>解决空回滚和业务悬挂问题，必须要记录当前事务状态，是在try、还是cancel？</p>
<h4 id="1）思路分析"><a href="#1）思路分析" class="headerlink" title="1）思路分析"></a>1）思路分析</h4><p>这里我们定义一张表：</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> `account_freeze_tbl` (</span><br><span class="line">  `xid` <span class="type">varchar</span>(<span class="number">128</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span>,</span><br><span class="line">  `user_id` <span class="type">varchar</span>(<span class="number">255</span>) <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;用户id&#x27;</span>,</span><br><span class="line">  `freeze_money` <span class="type">int</span>(<span class="number">11</span>) unsigned <span class="keyword">DEFAULT</span> <span class="string">&#x27;0&#x27;</span> COMMENT <span class="string">&#x27;冻结金额&#x27;</span>,</span><br><span class="line">  `state` <span class="type">int</span>(<span class="number">1</span>) <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;事务状态，0:try，1:confirm，2:cancel&#x27;</span>,</span><br><span class="line">  <span class="keyword">PRIMARY</span> KEY (`xid`) <span class="keyword">USING</span> BTREE</span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8 ROW_FORMAT<span class="operator">=</span>COMPACT;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>其中：</p>
<ul>
<li>xid：是全局事务id</li>
<li>freeze_money：用来记录用户冻结金额</li>
<li>state：用来记录事务状态</li>
</ul>
<p>那此时，我们的业务开怎么做呢？</p>
<ul>
<li>Try业务：<ul>
<li>记录冻结金额和事务状态到account_freeze表</li>
<li>扣减account表可用金额</li>
</ul>
</li>
<li>Confirm业务<ul>
<li>根据xid删除account_freeze表的冻结记录</li>
</ul>
</li>
<li>Cancel业务<ul>
<li>修改account_freeze表，冻结金额为0，state为2</li>
<li>修改account表，恢复可用金额</li>
</ul>
</li>
<li>如何判断是否空回滚？<ul>
<li>cancel业务中，根据xid查询account_freeze，如果为null则说明try还没做，需要空回滚</li>
</ul>
</li>
<li>如何避免业务悬挂？<ul>
<li>try业务中，根据xid查询account_freeze ，如果已经存在则证明Cancel已经执行，拒绝执行try业务</li>
</ul>
</li>
</ul>
<p>接下来，我们改造account-service，利用TCC实现余额扣减功能。</p>
<h4 id="2）声明TCC接口"><a href="#2）声明TCC接口" class="headerlink" title="2）声明TCC接口"></a>2）声明TCC接口</h4><p>TCC的Try、Confirm、Cancel方法都需要在接口中基于注解来声明，</p>
<p>我们在account-service项目中的<code>cn.itcast.account.service</code>包中新建一个接口，声明TCC三个接口：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> cn.itcast.account.service;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.seata.rm.tcc.api.BusinessActionContext;</span><br><span class="line"><span class="keyword">import</span> io.seata.rm.tcc.api.BusinessActionContextParameter;</span><br><span class="line"><span class="keyword">import</span> io.seata.rm.tcc.api.LocalTCC;</span><br><span class="line"><span class="keyword">import</span> io.seata.rm.tcc.api.TwoPhaseBusinessAction;</span><br><span class="line"></span><br><span class="line"><span class="meta">@LocalTCC</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">AccountTCCService</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@TwoPhaseBusinessAction(name = &quot;deduct&quot;, commitMethod = &quot;confirm&quot;, rollbackMethod = &quot;cancel&quot;)</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">deduct</span><span class="params">(<span class="meta">@BusinessActionContextParameter(paramName = &quot;userId&quot;)</span> String userId,</span></span><br><span class="line"><span class="params">                <span class="meta">@BusinessActionContextParameter(paramName = &quot;money&quot;)</span><span class="type">int</span> money)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">confirm</span><span class="params">(BusinessActionContext ctx)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">cancel</span><span class="params">(BusinessActionContext ctx)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h4 id="3）编写实现类"><a href="#3）编写实现类" class="headerlink" title="3）编写实现类"></a>3）编写实现类</h4><p>在account-service服务中的<code>cn.itcast.account.service.impl</code>包下新建一个类，实现TCC业务：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> cn.itcast.account.service.impl;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> cn.itcast.account.entity.AccountFreeze;</span><br><span class="line"><span class="keyword">import</span> cn.itcast.account.mapper.AccountFreezeMapper;</span><br><span class="line"><span class="keyword">import</span> cn.itcast.account.mapper.AccountMapper;</span><br><span class="line"><span class="keyword">import</span> cn.itcast.account.service.AccountTCCService;</span><br><span class="line"><span class="keyword">import</span> io.seata.core.context.RootContext;</span><br><span class="line"><span class="keyword">import</span> io.seata.rm.tcc.api.BusinessActionContext;</span><br><span class="line"><span class="keyword">import</span> lombok.extern.slf4j.Slf4j;</span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.factory.annotation.Autowired;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Service;</span><br><span class="line"><span class="keyword">import</span> org.springframework.transaction.annotation.Transactional;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AccountTCCServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">AccountTCCService</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> AccountMapper accountMapper;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> AccountFreezeMapper freezeMapper;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="meta">@Transactional</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">deduct</span><span class="params">(String userId, <span class="type">int</span> money)</span> &#123;</span><br><span class="line">        <span class="comment">// 0.获取事务id</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">xid</span> <span class="operator">=</span> RootContext.getXID();</span><br><span class="line">        <span class="comment">// 1.扣减可用余额</span></span><br><span class="line">        accountMapper.deduct(userId, money);</span><br><span class="line">        <span class="comment">// 2.记录冻结金额，事务状态</span></span><br><span class="line">        <span class="type">AccountFreeze</span> <span class="variable">freeze</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AccountFreeze</span>();</span><br><span class="line">        freeze.setUserId(userId);</span><br><span class="line">        freeze.setFreezeMoney(money);</span><br><span class="line">        freeze.setState(AccountFreeze.State.TRY);</span><br><span class="line">        freeze.setXid(xid);</span><br><span class="line">        freezeMapper.insert(freeze);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">confirm</span><span class="params">(BusinessActionContext ctx)</span> &#123;</span><br><span class="line">        <span class="comment">// 1.获取事务id</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">xid</span> <span class="operator">=</span> ctx.getXid();</span><br><span class="line">        <span class="comment">// 2.根据id删除冻结记录</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> freezeMapper.deleteById(xid);</span><br><span class="line">        <span class="keyword">return</span> count == <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">cancel</span><span class="params">(BusinessActionContext ctx)</span> &#123;</span><br><span class="line">        <span class="comment">// 0.查询冻结记录</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">xid</span> <span class="operator">=</span> ctx.getXid();</span><br><span class="line">        <span class="type">AccountFreeze</span> <span class="variable">freeze</span> <span class="operator">=</span> freezeMapper.selectById(xid);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 1.恢复可用余额</span></span><br><span class="line">        accountMapper.refund(freeze.getUserId(), freeze.getFreezeMoney());</span><br><span class="line">        <span class="comment">// 2.将冻结金额清零，状态改为CANCEL</span></span><br><span class="line">        freeze.setFreezeMoney(<span class="number">0</span>);</span><br><span class="line">        freeze.setState(AccountFreeze.State.CANCEL);</span><br><span class="line">        <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> freezeMapper.updateById(freeze);</span><br><span class="line">        <span class="keyword">return</span> count == <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>







<h2 id="4-4-SAGA模式"><a href="#4-4-SAGA模式" class="headerlink" title="4.4.SAGA模式"></a>4.4.SAGA模式</h2><p>Saga 模式是 Seata 即将开源的长事务解决方案，将由蚂蚁金服主要贡献。</p>
<p>其理论基础是Hector &amp; Kenneth  在1987年发表的论文<a target="_blank" rel="noopener" href="https://microservices.io/patterns/data/saga.html">Sagas</a>。</p>
<p>Seata官网对于Saga的指南：<a target="_blank" rel="noopener" href="https://seata.io/zh-cn/docs/user/saga.html">https://seata.io/zh-cn/docs/user/saga.html</a></p>
<h3 id="4-4-1-原理"><a href="#4-4-1-原理" class="headerlink" title="4.4.1.原理"></a>4.4.1.原理</h3><p>在 Saga 模式下，分布式事务内有多个参与者，每一个参与者都是一个冲正补偿服务，需要用户根据业务场景实现其正向操作和逆向回滚操作。</p>
<p>分布式事务执行过程中，依次执行各参与者的正向操作，如果所有正向操作均执行成功，那么分布式事务提交。如果任何一个正向操作执行失败，那么分布式事务会去退回去执行前面各参与者的逆向回滚操作，回滚已提交的参与者，使分布式事务回到初始状态。</p>
<p><img src="/xlrblog/assets/image-20210724184846396.png" alt="image-20210724184846396"></p>
<p>Saga也分为两个阶段：</p>
<ul>
<li>一阶段：直接提交本地事务</li>
<li>二阶段：成功则什么都不做；失败则通过编写补偿业务来回滚</li>
</ul>
<h3 id="4-4-2-优缺点"><a href="#4-4-2-优缺点" class="headerlink" title="4.4.2.优缺点"></a>4.4.2.优缺点</h3><p>优点：</p>
<ul>
<li>事务参与者可以基于事件驱动实现异步调用，吞吐高</li>
<li>一阶段直接提交事务，无锁，性能好</li>
<li>不用编写TCC中的三个阶段，实现简单</li>
</ul>
<p>缺点：</p>
<ul>
<li>软状态持续时间不确定，时效性差</li>
<li>没有锁，没有事务隔离，会有脏写</li>
</ul>
<h2 id="4-5-四种模式对比"><a href="#4-5-四种模式对比" class="headerlink" title="4.5.四种模式对比"></a>4.5.四种模式对比</h2><p>我们从以下几个方面来对比四种实现：</p>
<ul>
<li>一致性：能否保证事务的一致性？强一致还是最终一致？</li>
<li>隔离性：事务之间的隔离性如何？</li>
<li>代码侵入：是否需要对业务代码改造？</li>
<li>性能：有无性能损耗？</li>
<li>场景：常见的业务场景</li>
</ul>
<p>如图：</p>
<p><img src="/xlrblog/assets/image-20210724185021819.png" alt="image-20210724185021819"></p>
<h1 id="5-高可用"><a href="#5-高可用" class="headerlink" title="5.高可用"></a>5.高可用</h1><p>Seata的TC服务作为分布式事务核心，一定要保证集群的高可用性。</p>
<h2 id="5-1-高可用架构模型"><a href="#5-1-高可用架构模型" class="headerlink" title="5.1.高可用架构模型"></a>5.1.高可用架构模型</h2><p>搭建TC服务集群非常简单，启动多个TC服务，注册到nacos即可。</p>
<p>但集群并不能确保100%安全，万一集群所在机房故障怎么办？所以如果要求较高，一般都会做异地多机房容灾。</p>
<p>比如一个TC集群在上海，另一个TC集群在杭州：</p>
<p><img src="/xlrblog/assets/image-20210724185240957.png" alt="image-20210724185240957"></p>
<p>微服务基于事务组（tx-service-group)与TC集群的映射关系，来查找当前应该使用哪个TC集群。当SH集群故障时，只需要将vgroup-mapping中的映射关系改成HZ。则所有微服务就会切换到HZ的TC集群了。</p>
<h2 id="5-2-实现高可用"><a href="#5-2-实现高可用" class="headerlink" title="5.2.实现高可用"></a>5.2.实现高可用</h2><h3 id="TC服务的高可用和异地容灾"><a href="#TC服务的高可用和异地容灾" class="headerlink" title="TC服务的高可用和异地容灾"></a>TC服务的高可用和异地容灾</h3><h4 id="模拟异地容灾的TC集群"><a href="#模拟异地容灾的TC集群" class="headerlink" title="模拟异地容灾的TC集群"></a>模拟异地容灾的TC集群</h4><p>计划启动两台seata的tc服务节点：</p>
<table>
<thead>
<tr>
<th>节点名称</th>
<th>ip地址</th>
<th>端口号</th>
<th>集群名称</th>
</tr>
</thead>
<tbody><tr>
<td>seata</td>
<td>127.0.0.1</td>
<td>8091</td>
<td>SH</td>
</tr>
<tr>
<td>seata2</td>
<td>127.0.0.1</td>
<td>8092</td>
<td>HZ</td>
</tr>
</tbody></table>
<p>之前我们已经启动了一台seata服务，端口是8091，集群名为SH。</p>
<p>现在，将seata目录复制一份，起名为seata2</p>
<p>修改seata2&#x2F;conf&#x2F;registry.conf内容如下：</p>
<figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">registry</span> &#123;</span><br><span class="line">  <span class="comment"># tc服务的注册中心类，这里选择nacos，也可以是eureka、zookeeper等</span></span><br><span class="line">  <span class="attribute">type</span> = <span class="string">&quot;nacos&quot;</span></span><br><span class="line"></span><br><span class="line">  nacos &#123;</span><br><span class="line">    <span class="comment"># seata tc 服务注册到 nacos的服务名称，可以自定义</span></span><br><span class="line">    <span class="attribute">application</span> = <span class="string">&quot;seata-tc-server&quot;</span></span><br><span class="line">    serverAddr = <span class="string">&quot;127.0.0.1:8848&quot;</span></span><br><span class="line">    group = <span class="string">&quot;DEFAULT_GROUP&quot;</span></span><br><span class="line">    namespace = <span class="string">&quot;&quot;</span></span><br><span class="line">    cluster = <span class="string">&quot;HZ&quot;</span></span><br><span class="line">    username = <span class="string">&quot;nacos&quot;</span></span><br><span class="line">    password = <span class="string">&quot;nacos&quot;</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">config &#123;</span><br><span class="line">  <span class="comment"># 读取tc服务端的配置文件的方式，这里是从nacos配置中心读取，这样如果tc是集群，可以共享配置</span></span><br><span class="line">  <span class="attribute">type</span> = <span class="string">&quot;nacos&quot;</span></span><br><span class="line">  <span class="comment"># 配置nacos地址等信息</span></span><br><span class="line">  nacos &#123;</span><br><span class="line">    <span class="attribute">serverAddr</span> = <span class="string">&quot;127.0.0.1:8848&quot;</span></span><br><span class="line">    namespace = <span class="string">&quot;&quot;</span></span><br><span class="line">    group = <span class="string">&quot;SEATA_GROUP&quot;</span></span><br><span class="line">    username = <span class="string">&quot;nacos&quot;</span></span><br><span class="line">    password = <span class="string">&quot;nacos&quot;</span></span><br><span class="line">    dataId = <span class="string">&quot;seataServer.properties&quot;</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>进入seata2&#x2F;bin目录，然后运行命令：</p>
<figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">seata<span class="literal">-server</span>.bat <span class="literal">-p</span> <span class="number">8092</span></span><br></pre></td></tr></table></figure>

<p>打开nacos控制台，查看服务列表：</p>
<p>点进详情查看：</p>
<h4 id="将事务组映射配置到nacos"><a href="#将事务组映射配置到nacos" class="headerlink" title="将事务组映射配置到nacos"></a>将事务组映射配置到nacos</h4><p>接下来，我们需要将tx-service-group与cluster的映射关系都配置到nacos配置中心。</p>
<p>新建一个配置：</p>
<p>配置的内容如下：</p>
<p>Data ID:client.properties</p>
<p>Group:SEATA_GROUP</p>
<figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 事务组映射关系</span></span><br><span class="line"><span class="attr">service.vgroupMapping.seata-demo</span>=<span class="string">SH</span></span><br><span class="line"></span><br><span class="line"><span class="attr">service.enableDegrade</span>=<span class="string">false</span></span><br><span class="line"><span class="attr">service.disableGlobalTransaction</span>=<span class="string">false</span></span><br><span class="line"><span class="comment"># 与TC服务的通信配置</span></span><br><span class="line"><span class="attr">transport.type</span>=<span class="string">TCP</span></span><br><span class="line"><span class="attr">transport.server</span>=<span class="string">NIO</span></span><br><span class="line"><span class="attr">transport.heartbeat</span>=<span class="string">true</span></span><br><span class="line"><span class="attr">transport.enableClientBatchSendRequest</span>=<span class="string">false</span></span><br><span class="line"><span class="attr">transport.threadFactory.bossThreadPrefix</span>=<span class="string">NettyBoss</span></span><br><span class="line"><span class="attr">transport.threadFactory.workerThreadPrefix</span>=<span class="string">NettyServerNIOWorker</span></span><br><span class="line"><span class="attr">transport.threadFactory.serverExecutorThreadPrefix</span>=<span class="string">NettyServerBizHandler</span></span><br><span class="line"><span class="attr">transport.threadFactory.shareBossWorker</span>=<span class="string">false</span></span><br><span class="line"><span class="attr">transport.threadFactory.clientSelectorThreadPrefix</span>=<span class="string">NettyClientSelector</span></span><br><span class="line"><span class="attr">transport.threadFactory.clientSelectorThreadSize</span>=<span class="string">1</span></span><br><span class="line"><span class="attr">transport.threadFactory.clientWorkerThreadPrefix</span>=<span class="string">NettyClientWorkerThread</span></span><br><span class="line"><span class="attr">transport.threadFactory.bossThreadSize</span>=<span class="string">1</span></span><br><span class="line"><span class="attr">transport.threadFactory.workerThreadSize</span>=<span class="string">default</span></span><br><span class="line"><span class="attr">transport.shutdown.wait</span>=<span class="string">3</span></span><br><span class="line"><span class="comment"># RM配置</span></span><br><span class="line"><span class="attr">client.rm.asyncCommitBufferLimit</span>=<span class="string">10000</span></span><br><span class="line"><span class="attr">client.rm.lock.retryInterval</span>=<span class="string">10</span></span><br><span class="line"><span class="attr">client.rm.lock.retryTimes</span>=<span class="string">30</span></span><br><span class="line"><span class="attr">client.rm.lock.retryPolicyBranchRollbackOnConflict</span>=<span class="string">true</span></span><br><span class="line"><span class="attr">client.rm.reportRetryCount</span>=<span class="string">5</span></span><br><span class="line"><span class="attr">client.rm.tableMetaCheckEnable</span>=<span class="string">false</span></span><br><span class="line"><span class="attr">client.rm.tableMetaCheckerInterval</span>=<span class="string">60000</span></span><br><span class="line"><span class="attr">client.rm.sqlParserType</span>=<span class="string">druid</span></span><br><span class="line"><span class="attr">client.rm.reportSuccessEnable</span>=<span class="string">false</span></span><br><span class="line"><span class="attr">client.rm.sagaBranchRegisterEnable</span>=<span class="string">false</span></span><br><span class="line"><span class="comment"># TM配置</span></span><br><span class="line"><span class="attr">client.tm.commitRetryCount</span>=<span class="string">5</span></span><br><span class="line"><span class="attr">client.tm.rollbackRetryCount</span>=<span class="string">5</span></span><br><span class="line"><span class="attr">client.tm.defaultGlobalTransactionTimeout</span>=<span class="string">60000</span></span><br><span class="line"><span class="attr">client.tm.degradeCheck</span>=<span class="string">false</span></span><br><span class="line"><span class="attr">client.tm.degradeCheckAllowTimes</span>=<span class="string">10</span></span><br><span class="line"><span class="attr">client.tm.degradeCheckPeriod</span>=<span class="string">2000</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># undo日志配置</span></span><br><span class="line"><span class="attr">client.undo.dataValidation</span>=<span class="string">true</span></span><br><span class="line"><span class="attr">client.undo.logSerialization</span>=<span class="string">jackson</span></span><br><span class="line"><span class="attr">client.undo.onlyCareUpdateColumns</span>=<span class="string">true</span></span><br><span class="line"><span class="attr">client.undo.logTable</span>=<span class="string">undo_log</span></span><br><span class="line"><span class="attr">client.undo.compress.enable</span>=<span class="string">true</span></span><br><span class="line"><span class="attr">client.undo.compress.type</span>=<span class="string">zip</span></span><br><span class="line"><span class="attr">client.undo.compress.threshold</span>=<span class="string">64k</span></span><br><span class="line"><span class="attr">client.log.exceptionRate</span>=<span class="string">100</span></span><br></pre></td></tr></table></figure>

<h4 id="微服务读取nacos配置"><a href="#微服务读取nacos配置" class="headerlink" title="微服务读取nacos配置"></a>微服务读取nacos配置</h4><p>接下来，需要修改每一个微服务的application.yml文件，让微服务读取nacos中的client.properties文件：</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">seata:</span></span><br><span class="line">  <span class="attr">config:</span></span><br><span class="line">    <span class="attr">type:</span> <span class="string">nacos</span></span><br><span class="line">    <span class="attr">nacos:</span></span><br><span class="line">      <span class="attr">server-addr:</span> <span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span><span class="string">:8848</span></span><br><span class="line">      <span class="attr">username:</span> <span class="string">nacos</span></span><br><span class="line">      <span class="attr">password:</span> <span class="string">nacos</span></span><br><span class="line">      <span class="attr">group:</span> <span class="string">SEATA_GROUP</span></span><br><span class="line">      <span class="attr">data-id:</span> <span class="string">client.properties</span></span><br></pre></td></tr></table></figure>



<p>重启微服务，现在微服务到底是连接tc的SH集群，还是tc的HZ集群，都统一由nacos的client.properties来决定了。</p>

    </div>

    
    
    

      <footer class="post-footer">
          <div class="post-tags">
              <a href="/xlrblog/tags/SpringCloud/" rel="tag"># SpringCloud</a>
          </div>

        


        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/xlrblog/2023/10/23/%E5%BE%AE%E6%9C%8D%E5%8A%A1%E4%BF%9D%E6%8A%A4/" rel="prev" title="微服务保护">
      <i class="fa fa-chevron-left"></i> 微服务保护
    </a></div>
      <div class="post-nav-item">
    <a href="/xlrblog/2023/11/04/%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98/" rel="next" title="分布式缓存">
      分布式缓存 <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#1-%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E9%97%AE%E9%A2%98"><span class="nav-number">1.</span> <span class="nav-text">1.分布式事务问题</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-1-%E6%9C%AC%E5%9C%B0%E4%BA%8B%E5%8A%A1"><span class="nav-number">1.1.</span> <span class="nav-text">1.1.本地事务</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#1-2-%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1"><span class="nav-number">1.2.</span> <span class="nav-text">1.2.分布式事务</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#1-3-%E6%BC%94%E7%A4%BA%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E9%97%AE%E9%A2%98"><span class="nav-number">1.3.</span> <span class="nav-text">1.3.演示分布式事务问题</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#2-%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80"><span class="nav-number">2.</span> <span class="nav-text">2.理论基础</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#2-1-CAP%E5%AE%9A%E7%90%86"><span class="nav-number">2.1.</span> <span class="nav-text">2.1.CAP定理</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#2-1-1-%E4%B8%80%E8%87%B4%E6%80%A7"><span class="nav-number">2.1.1.</span> <span class="nav-text">2.1.1.一致性</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-1-2-%E5%8F%AF%E7%94%A8%E6%80%A7"><span class="nav-number">2.1.2.</span> <span class="nav-text">2.1.2.可用性</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-1-3-%E5%88%86%E5%8C%BA%E5%AE%B9%E9%94%99"><span class="nav-number">2.1.3.</span> <span class="nav-text">2.1.3.分区容错</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-1-4-%E7%9F%9B%E7%9B%BE"><span class="nav-number">2.1.4.</span> <span class="nav-text">2.1.4.矛盾</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-2-BASE%E7%90%86%E8%AE%BA"><span class="nav-number">2.2.</span> <span class="nav-text">2.2.BASE理论</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-3-%E8%A7%A3%E5%86%B3%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E7%9A%84%E6%80%9D%E8%B7%AF"><span class="nav-number">2.3.</span> <span class="nav-text">2.3.解决分布式事务的思路</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#3-%E5%88%9D%E8%AF%86Seata"><span class="nav-number">3.</span> <span class="nav-text">3.初识Seata</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#3-1-Seata%E7%9A%84%E6%9E%B6%E6%9E%84"><span class="nav-number">3.1.</span> <span class="nav-text">3.1.Seata的架构</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-2-%E9%83%A8%E7%BD%B2TC%E6%9C%8D%E5%8A%A1"><span class="nav-number">3.2.</span> <span class="nav-text">3.2.部署TC服务</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-3-%E5%BE%AE%E6%9C%8D%E5%8A%A1%E9%9B%86%E6%88%90Seata"><span class="nav-number">3.3.</span> <span class="nav-text">3.3.微服务集成Seata</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#3-3-1-%E5%BC%95%E5%85%A5%E4%BE%9D%E8%B5%96"><span class="nav-number">3.3.1.</span> <span class="nav-text">3.3.1.引入依赖</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-3-2-%E9%85%8D%E7%BD%AETC%E5%9C%B0%E5%9D%80"><span class="nav-number">3.3.2.</span> <span class="nav-text">3.3.2.配置TC地址</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-3-3-%E5%85%B6%E5%AE%83%E6%9C%8D%E5%8A%A1"><span class="nav-number">3.3.3.</span> <span class="nav-text">3.3.3.其它服务</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#4-%E5%8A%A8%E6%89%8B%E5%AE%9E%E8%B7%B5"><span class="nav-number">4.</span> <span class="nav-text">4.动手实践</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#4-1-XA%E6%A8%A1%E5%BC%8F"><span class="nav-number">4.1.</span> <span class="nav-text">4.1.XA模式</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#4-1-1-%E4%B8%A4%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4"><span class="nav-number">4.1.1.</span> <span class="nav-text">4.1.1.两阶段提交</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-1-2-Seata%E7%9A%84XA%E6%A8%A1%E5%9E%8B"><span class="nav-number">4.1.2.</span> <span class="nav-text">4.1.2.Seata的XA模型</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-1-3-%E4%BC%98%E7%BC%BA%E7%82%B9"><span class="nav-number">4.1.3.</span> <span class="nav-text">4.1.3.优缺点</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-1-4-%E5%AE%9E%E7%8E%B0XA%E6%A8%A1%E5%BC%8F"><span class="nav-number">4.1.4.</span> <span class="nav-text">4.1.4.实现XA模式</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-2-AT%E6%A8%A1%E5%BC%8F"><span class="nav-number">4.2.</span> <span class="nav-text">4.2.AT模式</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#4-2-1-Seata%E7%9A%84AT%E6%A8%A1%E5%9E%8B"><span class="nav-number">4.2.1.</span> <span class="nav-text">4.2.1.Seata的AT模型</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-2-2-%E6%B5%81%E7%A8%8B%E6%A2%B3%E7%90%86"><span class="nav-number">4.2.2.</span> <span class="nav-text">4.2.2.流程梳理</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-2-3-AT%E4%B8%8EXA%E7%9A%84%E5%8C%BA%E5%88%AB"><span class="nav-number">4.2.3.</span> <span class="nav-text">4.2.3.AT与XA的区别</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-2-4-%E8%84%8F%E5%86%99%E9%97%AE%E9%A2%98"><span class="nav-number">4.2.4.</span> <span class="nav-text">4.2.4.脏写问题</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-2-5-%E4%BC%98%E7%BC%BA%E7%82%B9"><span class="nav-number">4.2.5.</span> <span class="nav-text">4.2.5.优缺点</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-2-6-%E5%AE%9E%E7%8E%B0AT%E6%A8%A1%E5%BC%8F"><span class="nav-number">4.2.6.</span> <span class="nav-text">4.2.6.实现AT模式</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-3-TCC%E6%A8%A1%E5%BC%8F"><span class="nav-number">4.3.</span> <span class="nav-text">4.3.TCC模式</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#4-3-1-%E6%B5%81%E7%A8%8B%E5%88%86%E6%9E%90"><span class="nav-number">4.3.1.</span> <span class="nav-text">4.3.1.流程分析</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-3-2-Seata%E7%9A%84TCC%E6%A8%A1%E5%9E%8B"><span class="nav-number">4.3.2.</span> <span class="nav-text">4.3.2.Seata的TCC模型</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-3-3-%E4%BC%98%E7%BC%BA%E7%82%B9"><span class="nav-number">4.3.3.</span> <span class="nav-text">4.3.3.优缺点</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-3-4-%E4%BA%8B%E5%8A%A1%E6%82%AC%E6%8C%82%E5%92%8C%E7%A9%BA%E5%9B%9E%E6%BB%9A"><span class="nav-number">4.3.4.</span> <span class="nav-text">4.3.4.事务悬挂和空回滚</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#1%EF%BC%89%E7%A9%BA%E5%9B%9E%E6%BB%9A"><span class="nav-number">4.3.4.1.</span> <span class="nav-text">1）空回滚</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#2%EF%BC%89%E4%B8%9A%E5%8A%A1%E6%82%AC%E6%8C%82"><span class="nav-number">4.3.4.2.</span> <span class="nav-text">2）业务悬挂</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-3-5-%E5%AE%9E%E7%8E%B0TCC%E6%A8%A1%E5%BC%8F"><span class="nav-number">4.3.5.</span> <span class="nav-text">4.3.5.实现TCC模式</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#1%EF%BC%89%E6%80%9D%E8%B7%AF%E5%88%86%E6%9E%90"><span class="nav-number">4.3.5.1.</span> <span class="nav-text">1）思路分析</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#2%EF%BC%89%E5%A3%B0%E6%98%8ETCC%E6%8E%A5%E5%8F%A3"><span class="nav-number">4.3.5.2.</span> <span class="nav-text">2）声明TCC接口</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#3%EF%BC%89%E7%BC%96%E5%86%99%E5%AE%9E%E7%8E%B0%E7%B1%BB"><span class="nav-number">4.3.5.3.</span> <span class="nav-text">3）编写实现类</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-4-SAGA%E6%A8%A1%E5%BC%8F"><span class="nav-number">4.4.</span> <span class="nav-text">4.4.SAGA模式</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#4-4-1-%E5%8E%9F%E7%90%86"><span class="nav-number">4.4.1.</span> <span class="nav-text">4.4.1.原理</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-4-2-%E4%BC%98%E7%BC%BA%E7%82%B9"><span class="nav-number">4.4.2.</span> <span class="nav-text">4.4.2.优缺点</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-5-%E5%9B%9B%E7%A7%8D%E6%A8%A1%E5%BC%8F%E5%AF%B9%E6%AF%94"><span class="nav-number">4.5.</span> <span class="nav-text">4.5.四种模式对比</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#5-%E9%AB%98%E5%8F%AF%E7%94%A8"><span class="nav-number">5.</span> <span class="nav-text">5.高可用</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#5-1-%E9%AB%98%E5%8F%AF%E7%94%A8%E6%9E%B6%E6%9E%84%E6%A8%A1%E5%9E%8B"><span class="nav-number">5.1.</span> <span class="nav-text">5.1.高可用架构模型</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5-2-%E5%AE%9E%E7%8E%B0%E9%AB%98%E5%8F%AF%E7%94%A8"><span class="nav-number">5.2.</span> <span class="nav-text">5.2.实现高可用</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#TC%E6%9C%8D%E5%8A%A1%E7%9A%84%E9%AB%98%E5%8F%AF%E7%94%A8%E5%92%8C%E5%BC%82%E5%9C%B0%E5%AE%B9%E7%81%BE"><span class="nav-number">5.2.1.</span> <span class="nav-text">TC服务的高可用和异地容灾</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E6%A8%A1%E6%8B%9F%E5%BC%82%E5%9C%B0%E5%AE%B9%E7%81%BE%E7%9A%84TC%E9%9B%86%E7%BE%A4"><span class="nav-number">5.2.1.1.</span> <span class="nav-text">模拟异地容灾的TC集群</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%B0%86%E4%BA%8B%E5%8A%A1%E7%BB%84%E6%98%A0%E5%B0%84%E9%85%8D%E7%BD%AE%E5%88%B0nacos"><span class="nav-number">5.2.1.2.</span> <span class="nav-text">将事务组映射配置到nacos</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%BE%AE%E6%9C%8D%E5%8A%A1%E8%AF%BB%E5%8F%96nacos%E9%85%8D%E7%BD%AE"><span class="nav-number">5.2.1.3.</span> <span class="nav-text">微服务读取nacos配置</span></a></li></ol></li></ol></li></ol></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="君不见"
      src="/xlrblog/images/avatar.gif">
  <p class="site-author-name" itemprop="name">君不见</p>
  <div class="site-description" itemprop="description">君不见，黄河之水天上来，奔流到海不复回。</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/xlrblog/archives/">
        
          <span class="site-state-item-count">50</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
        <span class="site-state-item-count">9</span>
        <span class="site-state-item-name">分类</span>
      </div>
      <div class="site-state-item site-state-tags">
        <span class="site-state-item-count">25</span>
        <span class="site-state-item-name">标签</span>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="https://github.com/decxlr" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;decxlr" rel="noopener" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
  </div>



      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2023</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">君不见</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://pisces.theme-next.org/" class="theme-link" rel="noopener" target="_blank">NexT.Pisces</a> 强力驱动
  </div>

        








      </div>
    </footer>
  </div>

  
  <script src="/xlrblog/lib/anime.min.js"></script>
  <script src="/xlrblog/lib/velocity/velocity.min.js"></script>
  <script src="/xlrblog/lib/velocity/velocity.ui.min.js"></script>

<script src="/xlrblog/js/utils.js"></script>

<script src="/xlrblog/js/motion.js"></script>


<script src="/xlrblog/js/schemes/pisces.js"></script>


<script src="/xlrblog/js/next-boot.js"></script>




  




  
<script src="/xlrblog/js/local-search.js"></script>













  

  

</body>
</html>
